#include <lpc2292.h>

#include <Defs.h>

#include <LCD_driver.h>
#include <lcdtext.h>
#include <Fonts.h>

unsigned char lcd_writechar(unsigned char pos, unsigned char character)
{
    unsigned char line = pos/LINE_WIDTH;
    unsigned char lpos = pos%LINE_WIDTH;
    unsigned char side = (lpos*FONT_WIDTH) >= (WIDTH/2);
    unsigned char j, d;

    if (line > 3)
        return ERR;
   
	lcd_setpage(line); 
    lcd_setcol(side, lpos*FONT_WIDTH - side*WIDTH/2);
	for(j=0; j<FONT_WIDTH; j++)
	{
		if( (lpos*FONT_WIDTH + j - (WIDTH/2))%WIDTH == 0)
		{
			side = 1;
			lcd_setcol(side, 0);
        }
		if((character >= ASCII_OFFSET) && (character <= ASCII_MAX)) // an alphanumeric symbol
			lcd_writebyte(side, font[(character - ASCII_OFFSET)*FONT_WIDTH + j]);
        else // else, substitute with a whitespace
			lcd_writebyte(side, font[(' ' - ASCII_OFFSET)*FONT_WIDTH + j]);
	}
    
	return OK;
}

unsigned char LCDWriteText(unsigned char pos, unsigned char len, unsigned char* buf)
{
    unsigned char line;
    unsigned char lpos;
    unsigned char nlines;
    unsigned char i, j;
    unsigned char side;
    unsigned char d = 0;

    line = pos/LINE_WIDTH;
    lpos = pos%LINE_WIDTH;
    nlines = (lpos+len)/LINE_WIDTH;
    side = (lpos*FONT_WIDTH) >= (WIDTH/2); // 0 - left, 1 - right
    
	if (line + nlines-1 > 3)
        return ERR;
    
	lcd_setcol(side, lpos*FONT_WIDTH - side*WIDTH/2);
    lcd_setpage(line);
    
    for(i=0; i<len; i++)
	{
	    if( ( ((lpos+i)%LINE_WIDTH) == 0 ) && (lpos + i != 0))
        {
            side = 0;
            line++;
            lcd_setcol(side, 0);
            lcd_setpage(line);
        }
        for(j=0; j<FONT_WIDTH; j++) // write symbol
        {
			if( ((lpos+i)%LINE_WIDTH)*FONT_WIDTH + j == WIDTH/2 )
            {
                side = 1;
                lcd_setcol(side, 0);
            }
            if((buf[i] >= ASCII_OFFSET) && (buf[i] <= ASCII_MAX))
                lcd_writebyte(side, font[(buf[i] - ASCII_OFFSET)*FONT_WIDTH + j]);
            else
                lcd_writebyte(side, font[(' ' - ASCII_OFFSET)*FONT_WIDTH + j]);
        }
    }
	
    return OK;
}

unsigned char lcd_status(unsigned char con)
{
    unsigned char* address;
	
    if(con > 1)
        return 0xFF;
    if(con == 0)
        address = (unsigned char *)(IND1_BASE_ADDRESS | ADDR_CMD);
    else
        address = (unsigned char *)(IND2_BASE_ADDRESS | ADDR_CMD);
    
	return *address;
}

static void lcd_writecmd(unsigned char cmd, unsigned char arg)
{
    unsigned char* address;
    
    address = (unsigned char *)(IND1_BASE_ADDRESS | ADDR_CMD);
  
	while(lcd_status(0) & 0x90);

	*address = cmd|arg;
    address = (unsigned char *)(IND2_BASE_ADDRESS | ADDR_CMD);
    
	while(lcd_status(1) & 0x90);
    
	*address = cmd|arg;
}

inline void lcd_onoff(unsigned char onoff)
{
    if(onoff > 1)
        return;
 
	lcd_writecmd(0xAE, onoff);
}


inline void lcd_adc(unsigned char inv)
{
    if(inv > 1)
        return;
    
	lcd_writecmd(0xA0, inv);
}

inline void lcd_stdrive(unsigned char onoff)
{
    if(onoff > 1)
        return;
    
	lcd_writecmd(0xA4, onoff);
}

inline void lcd_duty(unsigned char duty)
{
    if(duty > 1)
        return;
    
	lcd_writecmd(0xA8, duty);
}

inline void lcd_setpage(unsigned char page)
{
    if(page > 3)
        return;
		
    lcd_writecmd(0xB8, page);
}

inline void lcd_setstring(unsigned char n)
{
    if(n > 31)
        return;
		
    lcd_writecmd(0xC0, n);
}

void lcd_setcol(unsigned char side, unsigned char col)
{
    unsigned char* address;
	
    if( (side>1) || (col>WIDTH/2) )
        return;
    
	while(lcd_status(side) & 0x90);
    
	if ( side == 0 )
        address = (unsigned char *)(IND1_BASE_ADDRESS | ADDR_CMD);
    else
        address = (unsigned char *)(IND2_BASE_ADDRESS | ADDR_CMD);
    
	*address = col;
}

void lcd_writebyte(unsigned char side, unsigned char b)
{
    unsigned char* address;
  
	if( side > 1 )
        return;
    while(lcd_status(side) & 0x90);
   
	if ( side == 0 )
        address = (unsigned char *)(IND1_BASE_ADDRESS | ADDR_DATA);
    else
        address = (unsigned char *)(IND2_BASE_ADDRESS | ADDR_DATA);
   
	*address = b;
}

unsigned char lcd_readbyte(unsigned char side)
{
    unsigned char* address;
    
	if( side > 1 )
        return 0;
    
	while(lcd_status(side) & 0x90);
    
	if ( side == 0 )
        address = (unsigned char *)(IND1_BASE_ADDRESS | ADDR_DATA);
    else
        address = (unsigned char *)(IND2_BASE_ADDRESS | ADDR_DATA);
    
	return *address;
}
/*
void lcd_clear(unsigned char color)
{
    unsigned char mask;
    unsigned char i, j;

    mask = (color)?0xFF:0x00;
    
	for( i = 0; i < 4; i++ )
    {
        lcd_setpage(i);
        lcd_setcol(0, 0);
        lcd_setcol(1, 0);
    
		for( j = 0; j < WIDTH/2; j++)
        {
            lcd_writebyte(0, mask);
            lcd_writebyte(1, mask);
        }
    }
}*/

void lcd_scrolldown()
{
    volatile unsigned char data0, data1;
    unsigned char i, j;

    for(i = 1; i < 4; i++) // shift data one page upper (p1->p0, p2->p1, p3->p2)
    {
        for(j = 0; j < WIDTH/2; j++)
        {
            lcd_setpage(i);
            lcd_setcol(0, j);
            lcd_setcol(1, j);
            data0 = lcd_readbyte(0);
            data1 = lcd_readbyte(1);
            data0 = lcd_readbyte(0);
            data1 = lcd_readbyte(1);
            lcd_setpage(i-1);
            lcd_setcol(0, j);
            lcd_setcol(1, j);
            lcd_writebyte(0, data0);
            lcd_writebyte(1, data1);
        }
    }
        
	lcd_setpage(3); // clear last (lower) page
    lcd_setcol(0, 0);
    lcd_setcol(1, 0);
        
	for( j = 0; j < WIDTH/2; j++)
    { 
        lcd_writebyte(0, 0x00);
        lcd_writebyte(1, 0x00);
    }
}

void lcd_init()
{
    lcd_onoff(1);
    lcd_adc(0);
    lcd_stdrive(0);
    lcd_duty(1);
    lcd_setstring(0);
    LCDClear(0);
}
